package edu.umd.ion.wavemeter.server;

import edu.umd.ion.wavemeter.service.DynamicExposureParameters;
import edu.umd.ion.wavemeter.service.ServerParameters;
import edu.umd.ion.wavemeter.service.WavemeterChannelData;
import edu.umd.ion.wavemeter.service.WavemeterData;

public class DynamicExposureControl implements ExposureControl {
	private DynamicExposureParameters ExposureParams = new DynamicExposureParameters();
	private WavemeterData Data;
	private final int NumRawChannels = WavemeterConfiguration.NumRawChannels();
	private final int NumVirtChannels = WavemeterConfiguration.NumVirtChannels();
	int[][] RawExposure = new int[NumRawChannels][2];
	int[][] LastReportedExposure = new int[NumVirtChannels][2];
	int[][] LastReportedIntensity = new int[NumVirtChannels][2];
	long[][] LastReportedExposureLoopindex = new long[NumVirtChannels][2];
	int[] Counter1 = new int[NumVirtChannels];
	int[] Counter2 = new int[NumVirtChannels];
	
	public DynamicExposureControl( WavemeterData data ) {
		Data = data;
		for (int i=0; i<NumRawChannels; ++i) {
			WavemeterLibrary.SetExposureModeNum(i+1,false);
		}
		for (int i=0; i<NumVirtChannels; ++i) {
			LastReportedExposure[i][0] = LastReportedExposure[i][1] = 10;
			Counter1[i] = 0;
			Counter2[i] = 0;
		}
	}

	public void pushExposure(int rawchannel, int virtchannel) {
		try {
			int Exposure = Data.ChannelData[virtchannel].Exposure1;
			//if (RawExposure[rawchannel][0] != Exposure) {
				//System.out.println("push exposure array 1 "+Exposure+" to rawchannel "+rawchannel);
				RawExposure[rawchannel][0] = Exposure;	
				WavemeterLibrary.SetExposureNum(rawchannel+1, 1, Exposure );
				//if (result!=0) throw new WavemeterSetException("SetExposureMode",result);
			//}
			Exposure = Data.ChannelData[virtchannel].Exposure2;
			//if (RawExposure[rawchannel][1] != Exposure) {
				//System.out.println("push exposure array 2 "+Exposure+" to rawchannel "+rawchannel);
				RawExposure[rawchannel][1] = Exposure;
				WavemeterLibrary.SetExposureNum(rawchannel+1, 2, Exposure );
				//if (result!=0) throw new WavemeterSetException("SetExposureMode",result);
			//}
		}
		catch (Exception ex) {
		}
	}
	
	public int calculateNewExposure(int ReferenceExposure, int intensity) {
		int NewExposure = ReferenceExposure;
		if (intensity >= ExposureParams.MaxPossible) {
			NewExposure = ((ReferenceExposure*ExposureParams.ReduceToPercent)/100)-1;
		} else if (intensity>ExposureParams.MaxAdj) {
			NewExposure = ((ReferenceExposure*ExposureParams.Optimum)/intensity/2) + ReferenceExposure/2;
		} else if (intensity==0) {
			System.out.println("0 intensity");
			NewExposure = ((ReferenceExposure*ExposureParams.Optimum)/2)+ ReferenceExposure/2;					
		} else if (intensity<ExposureParams.MinAdj) {
			NewExposure = ((ReferenceExposure+1)*ExposureParams.Optimum)/intensity/2 + ReferenceExposure/2;
		}		
		return Math.min(NewExposure,ExposureParams.MaxExposure);
	}
	
	public void observedIntensity(int virtchannel, int array, int intensity, long loopindex ) {
		if (!Data.ChannelData[virtchannel].active) {
			return;
		}
		if (array==1) {
			if (Counter1[virtchannel]>=ExposureParams.SkipCount) {
				WavemeterChannelData ChannelData = Data.ChannelData[virtchannel];
				LastReportedIntensity[virtchannel][0] = intensity;
				if (ChannelData.AutoExposure) {
					int ReferenceExposure = ChannelData.Exposure1; //((loopindex-LastReportedExposureLoopindex[virtchannel][0])<100 ? 
							//LastReportedExposure[virtchannel][0] : ChannelData.Exposure1);
					int RelevantIntensity = (int) Math.max(intensity, 
							LastReportedIntensity[virtchannel][1]/(1+(ExposureParams.SecondInterferometerBalance*ChannelData.Exposure2)/ChannelData.Exposure1) );
					ChannelData.Exposure1 = Math.max(calculateNewExposure(ReferenceExposure,RelevantIntensity),
							ExposureParams.MinExposure);
					// 
					//System.out.println("observedIntensity virtchannel: "+virtchannel+" array: "+array+" intensity: "+
					//		intensity+" ReferenceExposure: "+ReferenceExposure+" new Exposure: "+
					//		ChannelData.Exposure1);
				}
				Counter1[virtchannel] = 0;
			} else {
				Counter1[virtchannel] += 1;
			}
		}
		else {
			if (Counter2[virtchannel]>=ExposureParams.SkipCount) {
				WavemeterChannelData ChannelData = Data.ChannelData[virtchannel];
				LastReportedIntensity[virtchannel][1] = intensity;
				if (ChannelData.AutoExposure) {
					int ReferenceExposure = ChannelData.Exposure2; //(loopindex-LastReportedExposureLoopindex[virtchannel][1])<100 ? LastReportedExposure[virtchannel][1] : ChannelData.Exposure2;
					ChannelData.Exposure2 = Math.max( calculateNewExposure(ReferenceExposure,intensity), 0);
					//System.out.println("observedIntensity virtchannel: "+virtchannel+" array: "+array+" intensity: "+
					//		intensity+" ReferenceExposure: "+ReferenceExposure+" new Exposure: "+
					//		ChannelData.Exposure2);
				}
				Counter2[virtchannel] = 0;
			} else {
				Counter2[virtchannel] += 1;
			}
		}
	}
	
	public void setExposure(int virtchannel, int array, int exposure ) {
		if (array==1) {
			Data.ChannelData[virtchannel].Exposure1 = exposure;
		} else {
			Data.ChannelData[virtchannel].Exposure2 = exposure;			
		}
	}
	
	public void setExposureMode(int virtchannel, boolean mode) {
		Data.ChannelData[virtchannel].AutoExposure = mode;
	}

	public void evaluateExposure(int virtchannel, int array, int value, long loopindex ) {
		LastReportedExposure[virtchannel][array] = value;
		LastReportedExposureLoopindex[virtchannel][array] = loopindex;
	}

	@Override
	public ServerParameters getParameters() {
		return ExposureParams;
	}

	@Override
	public ServerParameters setParameters(ServerParameters param) {
		return ExposureParams = (DynamicExposureParameters) param;
	}

	@Override
	public int getMaxExposure() {
		return ExposureParams.MaxExposure;
	}
}
